﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using CractalLibrary;


namespace GenerateDictionary
{
   public class CodeGenThreadTaskCPU :IDisposable
    {
       private List<string> paddingvalues;
       public delegate void RaiseBufferElementAddHandler(object sender, RaiseBufferElmentAddeventArgs e);
       public event RaiseBufferElementAddHandler OnBufferElementAdded;

       private delegate void aVoidDelegate();
       private delegate void withparamdelegate(string item);
       
       private cractal CodeGenCractal = new cractal();

       public string Filepath = "";
       
       public Queue<string> buffer;
       public int buffersize = 1000;

       public List<string> notification = new List<string>();

       //threading locks
       public object CractalLock;
       private object Bufferlock;
       private object Filelock;
       
       private char[] StartValue;
       private char[] EndValue; 
       public string ConfigCodeHeader = "";
       private Task processingThread;
       private System.Threading.CancellationToken CancelProcessing;
       private System.Threading.CancellationTokenSource tokensource;
       
       private System.IO.StreamWriter file;

       
       private aVoidDelegate Process;

       public int MaxCodeSize = -1;
       public int MinCodeSize = -1;
       private char[] valueset;

       public CodeGenThreadTaskCPU()
       {
           //threading locks
           CractalLock = new object();
           Bufferlock = new object();
           Filelock = new object();

           buffer = new Queue<string>(buffersize);
           Process = new aVoidDelegate(IncrementandStoreCracktal);
           
           tokensource = new System.Threading.CancellationTokenSource();
           CancelProcessing = tokensource.Token;
       }

       public CodeGenThreadTaskCPU(char[] valueset)
       {
           //threading locks
           CractalLock = new object();
           Bufferlock = new object();
           Filelock = new object();

           buffer = new Queue<string>(buffersize);
           Process = new aVoidDelegate(IncrementandStoreCracktal);

           tokensource = new System.Threading.CancellationTokenSource();
           CancelProcessing = tokensource.Token;
           this.valueset = valueset;
       }



       public void NewValueGenerated(object sender, IncrementEventArgs e)
       {

           StoreCracktal(e.Latestvalue,true);
           

       }

     

       public char[] SetStartValue
       {

           get
           {
               return StartValue;
           }
           set
           {
               StartValue = value;
           }
       }

       public char[] SetEndValue
       {
           get
           {
               return EndValue;
           }
           set
           {
               EndValue = value;
           }


       }

       public System.Threading.Tasks.TaskStatus ThreadState
       {
           get { return processingThread.Status; }

       }

       public void ShutdownThread()
       {
           if (!(notification.Contains(Utility.Utility.playingwithfiles) || notification.Contains(Utility.Utility.usershutdown) || notification.Contains(Utility.Utility.ranoutofspace)))
           {
               notification.Add(Utility.Utility.usershutdown);
               if (processingThread.Status == TaskStatus.Running || processingThread.Status == TaskStatus.WaitingToRun)
               {
                   tokensource.Cancel();
               }
           }

       }

       public void DiskspaceshutdownThread()
       {
           if (!(notification.Contains(Utility.Utility.playingwithfiles) || notification.Contains(Utility.Utility.usershutdown) || notification.Contains(Utility.Utility.ranoutofspace)))
           {
               notification.Add(Utility.Utility.ranoutofspace);
               if (processingThread.Status == TaskStatus.Running)
               {
                   tokensource.Cancel();
               }
           }

       }

       public void CodeGenStart()
       {
           file = new StreamWriter(Filepath, true);
           //StoreCracktal(ConfigCodeHeader,false);
           //FlushCractalBuffer();
           StoreinitialCodeGenValues(new string(StartValue), false);
          // StoreCracktal(new string(StartValue),false);
           FlushCractalBuffer();
           SetupCodeGenerator(valueset);
           processingThread = new Task(new Action(IncrementandStoreCracktal), tokensource.Token);
           GC.Collect();
           processingThread.Start();
          
          
       }

       private void StoreinitialCodeGenValues(string initilisationval, bool raisebufferelementaddedevent)
       {
           if (initilisationval.Length >= MinCodeSize)
           {
               StoreCracktal(initilisationval, raisebufferelementaddedevent);
           }

           int neededpadding = MaxCodeSize - initilisationval.Length;

           while (neededpadding > 0)
           {
               string currentval = initilisationval;
               currentval = paddingvalues.Where(z => z.Length == neededpadding).FirstOrDefault() + currentval;
               if (currentval.Length >= MinCodeSize)
               {
                   StoreCracktal(currentval,raisebufferelementaddedevent);
               }
               neededpadding--;
           }
       }

       private void assignValue(ref cractal CodeGenCractal, char[] StartValue)
       {
           for (int x = 0; x < CodeGenCractal.value.Length; x++)
           {
               CodeGenCractal.value[x] = StartValue[x];
           }
       }



       private void SetupCodeGenerator(char[] valueset)
       {
           CodeGenCractal = new cractal(valueset, MaxCodeSize, ref paddingvalues,MinCodeSize);
           // CodeGenCractal = new cractal();
           CodeGenCractal.value = new char[StartValue.Length];
           assignValue(ref CodeGenCractal, StartValue);    


           CodeGenCractal.OnIncremented += new cractal.IncrementedHandler(NewValueGenerated);
       }

       private void FlushCractalBuffer()
       {

           if (buffer.Count > 0)
           {

               while (buffer.Count > 0)
               {
                   try
                   {
                       file.WriteLine(buffer.Dequeue());
                       file.Flush();
                   }
                   catch
                   {

                       if (!(notification.Contains(Utility.Utility.playingwithfiles)))
                       {
                           notification.Add(Utility.Utility.playingwithfiles);
                           tokensource.Cancel();
                       }
                   }
               }
           }

          

       }
   

      

       private void StoreCracktal(string data,bool raisebufferevent)
       {
           if (raisebufferevent)
           {
               RaiseBufferElmentAddevent(data);

           }
               if (buffer.Count < buffersize)
               {
                   buffer.Enqueue(data);
               }
               else
               {
                   

                       while (buffer.Count > 0)
                       {
                           try
                           {
                               file.WriteLine(buffer.Dequeue());
                               file.Flush();
                           }
                           catch
                           {

                               if (!(notification.Contains(Utility.Utility.playingwithfiles)))
                               {
                                   notification.Add(Utility.Utility.playingwithfiles);
                                   tokensource.Cancel();
                               }

                           }
                       }
                       

                       buffer.Enqueue(data);
                   
               }
           }
       

       private void IncrementandStoreCracktal()
       {
          
               
               while (CompareCracktalnotEqual(ref CodeGenCractal.value,ref EndValue))
               {
                   
                       
                       if (CancelProcessing.IsCancellationRequested)
                       {
                           break;
                       }
                       CodeGenCractal.increment();
                   

               }

               if (!(CompareCracktalnotEqual(ref CodeGenCractal.value, ref EndValue)))
               {
                   notification.Add(Utility.Utility.completedcodegen);
               }

               FlushCractalBuffer();
               try
               {
                   file.Dispose();
               }
               catch { };
       }

       private bool CompareCracktalnotEqual(ref char[] p1, ref char[] p2)
       {
           if (p1.Length != p2.Length)
               return true;

           for (int x = 0; x < p1.Length; x++)
           {
               if (p1[x] != p2[x])
                   return true;

               
           }

           return false;
       }

       public class RaiseBufferElmentAddeventArgs : EventArgs
       {
           public string Latestvalue { get; private set; }

           public RaiseBufferElmentAddeventArgs(string lastv)
           {

               Latestvalue = lastv;
           }

           
       }

       private void RaiseBufferElmentAddevent(string lastval)
       {

           // Make sure someone is listening to event
           if (OnBufferElementAdded == null) return;

           RaiseBufferElmentAddeventArgs args = new RaiseBufferElmentAddeventArgs(lastval);
           OnBufferElementAdded(null, args);
       }


       internal void PaddingValues(ref List<string> Paddingvalues)
       {
           this.paddingvalues = Paddingvalues;
       }



       public  void Dispose()
       {
           Dispose(false);
           

       }

       private void Dispose(bool programaticallycalled)
       {
           if (programaticallycalled)
           {
               try
               {

                   file.Close();
                   paddingvalues.Clear();
               }
               catch { }
               
               file.Dispose();
               processingThread.Dispose();
               tokensource.Dispose();
               GC.SuppressFinalize(this);
           }
           else //GC doing this
           {
               try
               {

                   file.Close();
               }
               catch { }
               file.Dispose();
               processingThread.Dispose();
               tokensource.Dispose();
           }
       }
    }
}
